/*
 * serial.c
 *
 *  Created on: 2012-01-26
 *      Author: byron
 */
#include <FreeRTOS.h>
#include <queue.h>
#include <task.h>
#include <stdint.h>
#include "../LPC177x_8x.h"
#include "config.h"
#include "serial.h"



/* UART0 */
static xQueueHandle xRxedChars0;
static xQueueHandle xCharsForTx0;
static volatile long lTHREEmpty0;
static volatile long *plTHREEmpty0;

/* UART1 */
static xQueueHandle xRxedChars1;
static xQueueHandle xCharsForTx1;
static volatile long lTHREEmpty1;
static volatile long *plTHREEmpty1;

/* UART2 */
static xQueueHandle xRxedChars2;
static xQueueHandle xCharsForTx2;
static volatile long lTHREEmpty2;
static volatile long *plTHREEmpty2;

/* UART3 */
static xQueueHandle xRxedChars3;
static xQueueHandle xCharsForTx3;
static volatile long lTHREEmpty3;
static volatile long *plTHREEmpty3;

/* UART4 */
static xQueueHandle xRxedChars4;
static xQueueHandle xCharsForTx4;
static volatile long lTHREEmpty4;
static volatile long *plTHREEmpty4;

void vSerialISRCreateQueues(	unsigned portBASE_TYPE uxQueueLength, xQueueHandle *pxRxedChars,
								xQueueHandle *pxCharsForTx, long volatile *plTHREEmpty, long volatile **pplTHREEmptyFlag )
{
	/* Create the queues used to hold Rx and Tx characters. */
	*pxRxedChars = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( signed char ) );
	*pxCharsForTx = xQueueCreate( uxQueueLength + 1, ( unsigned portBASE_TYPE ) sizeof( signed char ) );

	/* Initialise the THRE empty flag - and pass back a reference. */
	*plTHREEmpty = ( long ) pdTRUE;
	*pplTHREEmptyFlag = plTHREEmpty;
}

void vSerialInit(int pxPort, uint32_t baud)
{
	unsigned int ulWantedClock, ulDivisor;
	ulWantedClock = baud * 16;
	ulDivisor = configCPU_CLOCK_HZ / ulWantedClock;

	if( pxPort == UART0 )
	{
		LPC_SC->PCONP |= 1 << 3;
		LPC_IOCON->P0_2 = (0b001);//TXD0
		LPC_IOCON->P0_3 = (0b001);//RXD0

		LPC_UART0->IER = 0x00;                         // disable all interrupts
		// set the baudrate
		LPC_UART0->LCR = 0x83;
		LPC_UART0->DLL = (uint8_t)ulDivisor;                // set for baud low byte
		LPC_UART0->DLM = (uint8_t)(ulDivisor >> 8);         // set for baud high byte
		LPC_UART0->LCR=0x03;
		LPC_UART0->FCR = 0x01;

		LPC_UART0->IER = 0x00000003; //RxTx interrupt
		NVIC_EnableIRQ(UART0_IRQn);
		NVIC_SetPriority(UART0_IRQn, configUART0_INTERRUPT_PRIORITY);
		vSerialISRCreateQueues(64, &xRxedChars0, &xCharsForTx0, &lTHREEmpty0, &plTHREEmpty0 );
	}
	else if( pxPort == UART1 )
	{
		LPC_SC->PCONP |= 1 << 4;
		LPC_IOCON->P0_15 = (0b001);//TXD1
		LPC_IOCON->P0_16 = (0b001);//RXD1
		LPC_UART1->IER = 0x00;                         // disable all interrupts
		// set the baudrate
		LPC_UART1->LCR = 0x83;
		LPC_UART1->DLL = (uint8_t)ulDivisor;                // set for baud low byte
		LPC_UART1->DLM = (uint8_t)(ulDivisor >> 8);         // set for baud high byte
		LPC_UART1->LCR=0x03;
		LPC_UART1->FCR = 0x01;

		LPC_UART1->IER = 0x00000003; //RxTx interrupt
		NVIC_EnableIRQ(UART1_IRQn);
		NVIC_SetPriority(UART1_IRQn, configUART1_INTERRUPT_PRIORITY);
		vSerialISRCreateQueues(64, &xRxedChars1, &xCharsForTx1, &lTHREEmpty1, &plTHREEmpty1 );
	}
	else if( pxPort == UART2 )
	{
		LPC_SC->PCONP |= 1 << 24;
		LPC_IOCON->P4_22 = (0b010); //TXD2
		LPC_IOCON->P4_23 = (0b010); //RXD2
		LPC_UART2->IER = 0x00;                         // disable all interrupts
		// set the baudrate
		LPC_UART2->LCR = 0x83;
		LPC_UART2->DLL = (uint8_t)ulDivisor;                // set for baud low byte
		LPC_UART2->DLM = (uint8_t)(ulDivisor >> 8);         // set for baud high byte
		LPC_UART2->LCR=0x03;
		LPC_UART2->FCR = 0x01;

		LPC_UART2->IER = 0x00000003; //RxTx interrupt
		NVIC_EnableIRQ(UART2_IRQn);
		NVIC_SetPriority(UART2_IRQn, configUART2_INTERRUPT_PRIORITY);
		vSerialISRCreateQueues(64, &xRxedChars2, &xCharsForTx2, &lTHREEmpty2, &plTHREEmpty2 );
	}
	else if( pxPort == UART3 )
	{
		LPC_SC->PCONP |= 1 << 25;
		LPC_IOCON->P0_0 = (0b010); //TXD3 and digital mode
		LPC_IOCON->P0_1 = (0b010); //RXD3 and digital mode
		LPC_UART3->IER = 0x00;                         // disable all interrupts
		// set the baudrate
		LPC_UART3->LCR = 0x83;
		LPC_UART3->DLL = (uint8_t)ulDivisor;                // set for baud low byte
		LPC_UART3->DLM = (uint8_t)(ulDivisor >> 8);         // set for baud high byte
		LPC_UART3->LCR=0x03;
		LPC_UART3->FCR = 0x01;

		LPC_UART3->IER = 0x00000003; //RxTx interrupt
		NVIC_EnableIRQ(UART3_IRQn);
		NVIC_SetPriority(UART3_IRQn, configUART3_INTERRUPT_PRIORITY);
		vSerialISRCreateQueues(64, &xRxedChars3, &xCharsForTx3, &lTHREEmpty3, &plTHREEmpty3 );
	}
	else if( pxPort == UART4 )
	{
		LPC_SC->PCONP |= 1 << 8;
		LPC_IOCON->P5_3 = (0b100); //RXD4
		LPC_IOCON->P5_4 = (0b100)|(1<<10); //TXD4 open drain
		LPC_UART4->IER = 0x00;                         // disable all interrupts
		// set the baudrate
		LPC_UART4->LCR = 0x83;
		LPC_UART4->DLL = (uint8_t)ulDivisor;                // set for baud low byte
		LPC_UART4->DLM = (uint8_t)(ulDivisor >> 8);         // set for baud high byte
		LPC_UART4->LCR=0x03;
		LPC_UART4->FCR = 0x01;

		LPC_UART4->IER = 0x00000003; //RxTx interrupt
		NVIC_EnableIRQ(UART4_IRQn);
		NVIC_SetPriority(UART4_IRQn, configUART3_INTERRUPT_PRIORITY);
		vSerialISRCreateQueues(64, &xRxedChars4, &xCharsForTx4, &lTHREEmpty4, &plTHREEmpty4 );
	}
}

void vSerialRebaud(int pxPort, uint32_t baud)
{
	unsigned int ulWantedClock, ulDivisor;
	unsigned char dummy;
	ulWantedClock = baud * 16;
	ulDivisor = configCPU_CLOCK_HZ / ulWantedClock;

	if( pxPort == UART4 )
	{
//		LPC_UART4->IER = 0x00;                         // disable all interrupts
		// set the baudrate
		LPC_UART4->LCR = 0x83;
		LPC_UART4->DLL = (uint8_t)ulDivisor;                // set for baud low byte
		LPC_UART4->DLM = (uint8_t)(ulDivisor >> 8);         // set for baud high byte
		LPC_UART4->LCR=0x03;
		LPC_UART4->FCR = 0x01;

//		LPC_UART4->IER = 0x00000003; //RxTx interrupt
	}

	while(xSerialGetChar( pxPort, &dummy, 1));
}

void UART0_IRQHandler(void)
{
signed char cChar;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

	/* What caused the interrupt? */
	switch( LPC_UART0->IIR & serINTERRUPT_SOURCE_MASK )
	{
		case serSOURCE_ERROR :	/* Not handling this, but clear the interrupt. */
								cChar = LPC_UART0->LSR;
								break;

		case serSOURCE_THRE	:	/* The THRE is empty.  If there is another
								character in the Tx queue, send it now. */
								if( xQueueReceiveFromISR( xCharsForTx0, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )
								{
									LPC_UART0->THR = cChar;
								}
								else
								{
									/* There are no further characters
									queued to send so we can indicate
									that the THRE is available. */
									lTHREEmpty0 = pdTRUE;
								}
							break;

		case serSOURCE_RX_TIMEOUT :
		case serSOURCE_RX	:	/* A character was received.  Place it in
								the queue of received characters. */
								cChar = LPC_UART0->RBR;
								xQueueSendFromISR( xRxedChars0, &cChar, &xHigherPriorityTaskWoken );
								break;

		default				:	/* There is nothing to do, leave the ISR. */
								break;
	}

	if( xHigherPriorityTaskWoken )
	{
		portYIELD();
	}
}

void UART1_IRQHandler(void)
{
signed char cChar;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

	/* What caused the interrupt? */
	switch( LPC_UART1->IIR & serINTERRUPT_SOURCE_MASK )
	{
		case serSOURCE_ERROR :	/* Not handling this, but clear the interrupt. */
								cChar = LPC_UART1->LSR;
								break;

		case serSOURCE_THRE	:	/* The THRE is empty.  If there is another
								character in the Tx queue, send it now. */
								if( xQueueReceiveFromISR( xCharsForTx1, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )
								{
									LPC_UART1->THR = cChar;
								}
								else
								{
									/* There are no further characters
									queued to send so we can indicate
									that the THRE is available. */
									lTHREEmpty1 = pdTRUE;
								}
							break;

		case serSOURCE_RX_TIMEOUT :
		case serSOURCE_RX	:	/* A character was received.  Place it in
								the queue of received characters. */
								cChar = LPC_UART1->RBR;
								xQueueSendFromISR( xRxedChars1, &cChar, &xHigherPriorityTaskWoken );
								break;

		default				:	/* There is nothing to do, leave the ISR. */
								break;
	}

	if( xHigherPriorityTaskWoken )
	{
		portYIELD();
	}
}

void UART2_IRQHandler(void)
{
signed char cChar;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

	/* What caused the interrupt? */
	switch( LPC_UART2->IIR & serINTERRUPT_SOURCE_MASK )
	{
		case serSOURCE_ERROR :	/* Not handling this, but clear the interrupt. */
								cChar = LPC_UART2->LSR;
								break;

		case serSOURCE_THRE	:	/* The THRE is empty.  If there is another
								character in the Tx queue, send it now. */
								if( xQueueReceiveFromISR( xCharsForTx2, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )
								{
									LPC_UART2->THR = cChar;
								}
								else
								{
									/* There are no further characters
									queued to send so we can indicate
									that the THRE is available. */
									lTHREEmpty2 = pdTRUE;
								}
							break;

		case serSOURCE_RX_TIMEOUT :
		case serSOURCE_RX	:	/* A character was received.  Place it in
								the queue of received characters. */
								cChar = LPC_UART2->RBR;
								xQueueSendFromISR( xRxedChars2, &cChar, &xHigherPriorityTaskWoken );
								break;

		default				:	/* There is nothing to do, leave the ISR. */
								break;
	}

	if( xHigherPriorityTaskWoken )
	{
		portYIELD();
	}
}

void UART3_IRQHandler(void)
{
signed char cChar;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

	/* What caused the interrupt? */
	switch( LPC_UART3->IIR & serINTERRUPT_SOURCE_MASK )
	{
		case serSOURCE_ERROR :	/* Not handling this, but clear the interrupt. */
								cChar = LPC_UART3->LSR;
								break;

		case serSOURCE_THRE	:	/* The THRE is empty.  If there is another
								character in the Tx queue, send it now. */
								if( xQueueReceiveFromISR( xCharsForTx3, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )
								{
									LPC_UART3->THR = cChar;
								}
								else
								{
									/* There are no further characters
									queued to send so we can indicate
									that the THRE is available. */
									lTHREEmpty3 = pdTRUE;
								}
							break;

		case serSOURCE_RX_TIMEOUT :
		case serSOURCE_RX	:	/* A character was received.  Place it in
								the queue of received characters. */
								cChar = LPC_UART3->RBR;
								xQueueSendFromISR( xRxedChars3, &cChar, &xHigherPriorityTaskWoken );
								break;

		default				:	/* There is nothing to do, leave the ISR. */
								break;
	}

	if( xHigherPriorityTaskWoken )
	{
		portYIELD();
	}
}

void UART4_IRQHandler(void)
{
signed char cChar;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

	/* What caused the interrupt? */
	switch( LPC_UART4->IIR & serINTERRUPT_SOURCE_MASK )
	{
		case serSOURCE_ERROR :	/* Not handling this, but clear the interrupt. */
								cChar = LPC_UART4->LSR;
								break;

		case serSOURCE_THRE	:	/* The THRE is empty.  If there is another
								character in the Tx queue, send it now. */
								if( xQueueReceiveFromISR( xCharsForTx4, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )
								{
									LPC_UART4->THR = cChar;
								}
								else
								{
									/* There are no further characters
									queued to send so we can indicate
									that the THRE is available. */
									lTHREEmpty4 = pdTRUE;
								}
							break;

		case serSOURCE_RX_TIMEOUT :
		case serSOURCE_RX	:	/* A character was received.  Place it in
								the queue of received characters. */
								cChar = LPC_UART4->RBR;
								xQueueSendFromISR( xRxedChars4, &cChar, &xHigherPriorityTaskWoken );
								break;

		default				:	/* There is nothing to do, leave the ISR. */
								break;
	}

	if( xHigherPriorityTaskWoken )
	{
		portYIELD();
	}
}

int xSerialGetChar( int pxPort, unsigned char *pcRxedChar, unsigned int xBlockTime )
{
	if(pxPort == UART0)
	{
		if( xQueueReceive( xRxedChars0, pcRxedChar, xBlockTime ) )
			return pdTRUE;
		else
			return pdFALSE;
	}
	else if(pxPort == UART1)
	{
		if( xQueueReceive( xRxedChars1, pcRxedChar, xBlockTime ) )
			return pdTRUE;
		else
			return pdFALSE;
	}
	else if(pxPort == UART2)
	{
		if( xQueueReceive( xRxedChars2, pcRxedChar, xBlockTime ) )
			return pdTRUE;
		else
			return pdFALSE;
	}
	else if(pxPort == UART3)
	{
		if( xQueueReceive( xRxedChars3, pcRxedChar, xBlockTime ) )
			return pdTRUE;
		else
			return pdFALSE;
	}
	else if(pxPort == UART4)
	{
		if( xQueueReceive( xRxedChars4, pcRxedChar, xBlockTime ) )
			return pdTRUE;
		else
			return pdFALSE;
	}
	return pdFALSE;
}

int xSerialPutChar( int pxPort, unsigned char cOutChar, unsigned int xBlockTime )
{
	int xReturn;

	if(pxPort == UART0)
	{
			if( *plTHREEmpty0 == ( long ) pdTRUE )
			{
				*plTHREEmpty0 = pdFALSE;
				LPC_UART0->THR = cOutChar;
				xReturn = pdPASS;
			}
			else
			{
				xReturn = xQueueSend( xCharsForTx0, &cOutChar, xBlockTime );

				if( ( *plTHREEmpty0 == ( long ) pdTRUE ) && ( xReturn == pdPASS ) )
				{
					xQueueReceive( xCharsForTx0, &cOutChar, 0);
					*plTHREEmpty0 = pdFALSE;
					LPC_UART0->THR = cOutChar;
				}
			}
	}
	else if(pxPort == UART1)
	{
			if( *plTHREEmpty1 == ( long ) pdTRUE )
			{
				*plTHREEmpty1 = pdFALSE;
				LPC_UART1->THR = cOutChar;
				xReturn = pdPASS;
			}
			else
			{
				xReturn = xQueueSend( xCharsForTx1, &cOutChar, xBlockTime );

				if( ( *plTHREEmpty1 == ( long ) pdTRUE ) && ( xReturn == pdPASS ) )
				{
					xQueueReceive( xCharsForTx1, &cOutChar, 0);
					*plTHREEmpty1 = pdFALSE;
					LPC_UART1->THR = cOutChar;
				}
			}
	}
	else if(pxPort == UART2)
	{
			if( *plTHREEmpty2 == ( long ) pdTRUE )
			{
				*plTHREEmpty2 = pdFALSE;
				LPC_UART2->THR = cOutChar;
				xReturn = pdPASS;
			}
			else
			{
				xReturn = xQueueSend( xCharsForTx2, &cOutChar, xBlockTime );

				if( ( *plTHREEmpty2 == ( long ) pdTRUE ) && ( xReturn == pdPASS ) )
				{
					xQueueReceive( xCharsForTx2, &cOutChar, 0);
					*plTHREEmpty2 = pdFALSE;
					LPC_UART2->THR = cOutChar;
				}
			}
	}
	else if(pxPort == UART3)
	{
			if( *plTHREEmpty3 == ( long ) pdTRUE )
			{
				*plTHREEmpty3 = pdFALSE;
				LPC_UART3->THR = cOutChar;
				xReturn = pdPASS;
			}
			else
			{
				xReturn = xQueueSend( xCharsForTx3, &cOutChar, xBlockTime );

				if( ( *plTHREEmpty3 == ( long ) pdTRUE ) && ( xReturn == pdPASS ) )
				{
					xQueueReceive( xCharsForTx3, &cOutChar, 0);
					*plTHREEmpty3 = pdFALSE;
					LPC_UART3->THR = cOutChar;
				}
			}
	}
	else if(pxPort == UART4)
	{
			if( *plTHREEmpty4 == ( long ) pdTRUE )
			{
				*plTHREEmpty4 = pdFALSE;
				LPC_UART4->THR = cOutChar;
				xReturn = pdPASS;
			}
			else
			{
				xReturn = xQueueSend( xCharsForTx4, &cOutChar, xBlockTime );

				if( ( *plTHREEmpty4 == ( long ) pdTRUE ) && ( xReturn == pdPASS ) )
				{
					xQueueReceive( xCharsForTx4, &cOutChar, 0);
					*plTHREEmpty4 = pdFALSE;
					LPC_UART4->THR = cOutChar;
				}
			}
	}
	return xReturn;
}


int xSerialPutString( int pxPort, const char * pcString, unsigned short usStringLength )
{
	while(*pcString)
	{
			xSerialPutChar( pxPort, *pcString++, 100 );
	}
	return usStringLength;
}

int xSerialPutData( int pxPort, unsigned char * pucData, unsigned short usDataLength )
{
	unsigned int i;
	for(i=0; i<usDataLength; i++)
	{
			if(xSerialPutChar( pxPort, pucData[i], 0 ) != pdPASS)
				return pdFAIL;
	}
	return pdPASS;
}
